import os
import time
import threading
import numpy as np
import tkinter as tk


class ShuDu(object):
    def __init__(self):
        self.win = tk.Tk()
        self.data_s = [[tk.StringVar() for i in range(9)] for j in range(9)]
        self.data_n = np.zeros((9, 9), dtype=np.uint8)
        self.data_g = self.data_n > 0
        self.flag = False
        self.draw()
        self.win.mainloop()

    def draw(self):
        f_data = tk.Frame(self.win)
        f_data.pack(ipady=3, ipadx=3)
        e_config = {'width': 3, 'justify': 'center'}
        for i in range(9):
            for j in range(9):
                tk.Entry(f_data, cnf=e_config, textvariable=self.data_s[i][j]).grid(row=i, column=j)
        f_button = tk.Frame(self.win)
        f_button.pack(ipady=3)
        tk.Button(f_button, width=8, text='Start', command=self.bt_start).pack(side='left')
        tk.Button(f_button, width=8, text='Load', command=self.bt_load).pack(side='left')
        tk.Button(f_button, width=8, text='Save', command=self.bt_save).pack(side='left')
        tk.Button(f_button, width=8, text='Clear', command=self.bt_clear).pack(side='left')

    def show_num(self):
        for i in range(9):
            for j in range(9):
                self.data_s[i][j].set(str(self.data_n[i][j]) if self.data_n[i][j] else '')

    def is_fit(self):
        for i in range(9):
            temp = [x for x in self.data_n[i, :] if x]
            if len(temp) != len(set(temp)):
                return False
        for i in range(9):
            temp = [x for x in self.data_n[:, i] if x]
            if len(temp) != len(set(temp)):
                return False
        for i in range(3):
            for j in range(3):
                temp = self.data_n[i*3:i*3+3, j*3:j*3+3].reshape(-1)
                temp = [x for x in temp if x]
                if len(temp) != len(set(temp)):
                    return False
        return True

    def show(self):
        while self.flag:
            time.sleep(0.25)
            self.show_num()
        print('show.end')

    def bt_start(self):
        self.flag = True
        threading.Thread(target=self.compute).start()
        # threading.Thread(target=self.show).start()

    def compute(self, r=0, c=0):
        if r == 9:
            self.show_num()
            self.flag = False
            print('Compute end')
            return True
        cc = c+1 if c < 8 else 0
        rr = r+1 if c == 8 else r
        if self.data_g[r, c]:
            return self.compute(rr, cc) if self.flag else True
        for i in range(1, 10):
            self.data_n[r, c] = i
            if not self.is_fit():
                continue
            if self.compute(rr, cc):
                break
        else:
            self.data_n[r, c] = 0
        # print(self.data_n)

    def bt_load(self):
        self.data_n = np.load('./save.npy')
        self.data_g = self.data_n > 0
        self.show_num()

    def bt_save(self):
        temp = np.array([[int(x.get()) if x.get() else 0 for x in y] for y in self.data_s], np.uint8)
        np.save('./save', temp)

    def bt_clear(self):
        self.data_n = np.zeros((9, 9), dtype=np.uint8)
        self.data_g = self.data_n > 0
        self.show_num()


if __name__ == '__main__':
    ShuDu()
